/**
 * @file
 */

#include <cstdint>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/stream.hpp>
#include "mocha/color.hpp"
#include "mocha/image.hpp"
#include "mocha/image_plugin.hpp"
#include "mocha/pointers.hpp"
#include "mocha/region.hpp"
#include "mocha/detail/image_buffer.hpp"

using namespace boost::filesystem;
using namespace boost::iostreams;

namespace mocha { namespace graphics {
image::image(plugin_factory_ptr_t factory, const std::string& name, const object_importer& importer): plugin_proxy<image_plugin>(factory, name, importer) {
}
image::~image() {
}
int image::load(const path& file) {
	return plugin()->load(file);
}
int image::load(std::istream& in, size_t size) {
	std::vector<std::istream::char_type> buffer(size);
	detail::fill_image_c_buffer(in, buffer);
	if (buffer.empty()) {
		return -1;
	}
	return plugin()->load(reinterpret_cast<std::int8_t*>(&buffer[0]), buffer.size());
}
int image::load(std::istream& in, size_t width, size_t height, const pixel_format& fmt) {
	std::vector<std::istream::char_type> buffer(image_buffer_size(width, height, fmt));
	detail::fill_image_c_buffer(in, buffer);
	if (buffer.empty()) {
		return -1;
	}
	return plugin()->load(reinterpret_cast<std::int8_t*>(&buffer[0]), width, height, fmt);
}
template<typename T>
int image::load(stream<T>& in, size_t size) {
	std::vector<typename T::char_type> buffer(size);
	detail::fill_image_c_buffer(in, buffer);
	if (buffer.empty()) {
		return -1;
	}
	return plugin()->load(reinterpret_cast<std::int8_t*>(&buffer[0]), buffer.size());
}
template<typename T>
int image::load(stream<T>& in, size_t width, size_t height, const pixel_format& fmt) {
	std::vector<typename T::char_type> buffer(image_buffer_size(width, height, fmt));
	detail::fill_image_c_buffer(in, buffer);
	if (buffer.empty()) {
		return -1;
	}
	return plugin()->load(reinterpret_cast<std::int8_t*>(&buffer[0]), width, height, fmt);
}
void image::free() {
	plugin()->free();
}
image_ptr_t image::clone() const {
	return image_ptr_t(); // TODO:
}
int image::crop(const region<int>& region) {
	return plugin()->crop(region);
}
int image::scale(size_t width, size_t height) {
	return plugin()->scale(width, height);
}
void image::conform() {
	pixel_format fmt = { { 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF }, mocha_pixel_ubyte /*...*/ };
	convert(fmt);
	size_t w = std::pow(2, std::ceil(std::log(std::max(width(), height()) / std::log(2))));
	scale(w, w);
}
int image::convert(const pixel_format& fmt) {
	return plugin()->convert(fmt);
}
array image::pixels() {
	std::int8_t* buffer = NULL;
	std::uint32_t size;
	plugin()->pixels(buffer, &size);
	return array(reinterpret_cast<char*>(buffer), size);
}
array_source image::pixels() const {
	const std::int8_t* buffer = NULL;
	std::uint32_t size;
	plugin()->pixels(buffer, &size);
	return array_source(reinterpret_cast<const char*>(buffer), size);
}
void image::raw(std::int8_t* data, size_t* size_out) {
	std::uint32_t size;
	plugin()->pixels(data, &size);
	if (size_out) {
		*size_out = size;
	}
}
int image::format(pixel_format& fmt) const {
	return plugin()->format(fmt);
}
size_t image::width() const {
	return plugin()->width();
}
size_t image::height() const {
	return plugin()->height();
}
bool image::loaded() const {
	return 0 != plugin()->loaded();
}
size_t image_buffer_size(size_t width, size_t height, const pixel_format& fmt) {
	return width * height * bytes_per_pixel(fmt);
}
template int32_t image::load<array_source>(stream<array_source>&, size_t);
template int32_t image::load<array_source>(stream<array_source>& in, size_t, size_t, const pixel_format&);
template int32_t image::load<file_source>(stream<file_source>&, size_t);
template int32_t image::load<file_source>(stream<file_source>& in, size_t, size_t, const pixel_format&);
}}

